package redecolaborativa.testes;

import java.util.Calendar;
import java.util.LinkedList;

import redecolaborativa.enums.*;
import redecolaborativa.excecoes.*;
import redecolaborativa.objetos.*;
import redecolaborativa.sistema.*;
import redecolaborativa.util.Visualizador;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;

public class TesteUsuario {
	private Usuario umUsuario;
	private Usuario outroUsuario;
	private Usuario outroUsuario2;
	private LinkedList<String> categorias = new LinkedList<String>();
	private LinkedList<String> outrasCategorias =  new LinkedList<String>();
	
	@Before
	public void iniciaTeste() throws Exception {
		umUsuario = new Usuario("Pedro", "pedroadmn", "Aristides Lobo");
		outroUsuario = new Usuario("Isabelly", "isabellylr", "Bodocongo");
		outroUsuario2 = new Usuario("Lilia", "liliars", "Santo Antonio");
		categorias.add("Filme"); categorias.add("Livro");
		outrasCategorias.add("Jogo"); outrasCategorias.add("Filme");		
	}

	@Test
	public void testaConstrutor() throws Exception {
		try {
			umUsuario = new Usuario("Pedro123", "Login", "Endereco");
			fail("Esperava excecao de nome invalido.");
		} catch(NomeInvalidoException ex) {
			assertEquals("Mensagem errada", "Nome inválido", ex.getMessage());
		}
		try {
			umUsuario = new Usuario(null, "Login", "Endereco");
			fail("Esperava excecao de nome invalido.");
		} catch(NomeInvalidoException ex) {
			assertEquals("Mensagem errada", "Nome inválido", ex.getMessage());
		}
		try {
			umUsuario = new Usuario(" ", "Login", "Endereco");
			fail("Esperava excecao de nome invalido.");
		} catch(NomeInvalidoException ex) {
			assertEquals("Mensagem errada", "Nome inválido", ex.getMessage());
		}
		try {
			umUsuario = new Usuario("Pedro", " ", "Endereco");
			fail("Esperava excecao de login invalido.");
		} catch(LoginInvalidoException ex) {
			assertEquals("Mensagem errada", "Login inválido", ex.getMessage());
		}
		try {
			umUsuario = new Usuario("Pedro", null, "Endereco");
			fail("Esperava excecao de login invalido.");
		} catch(LoginInvalidoException ex) {
			assertEquals("Mensagem errada", "Login inválido", ex.getMessage());
		}
		try {
			umUsuario = new Usuario("Pedro", "pedroadmn" , " ");
			fail("Esperava excecao de endereço invalido.");
		} catch(EnderecoInvalidoException ex) {
			assertEquals("Mensagem errada", "Endereço inválido", ex.getMessage());
		}
		try {
			umUsuario = new Usuario("Pedro", "pedroadmn", null);
			fail("Esperava excecao de login invalido.");
		} catch(EnderecoInvalidoException ex) {
			assertEquals("Mensagem errada", "Endereço inválido", ex.getMessage());
		}
	}
	
	@Test 
	public void testaGetDeUsuario() throws Exception{
		assertEquals("Nome errado", "Pedro", umUsuario.getNome());
		assertEquals("Login errado", "pedroadmn", umUsuario.getLogin());
		assertEquals("Endereco errado", "Aristides Lobo", umUsuario.getEndereco());
	}
	
	@Test
	public void testaCadastrarItem() throws Exception {		
		assertEquals(0, umUsuario.gerenteDeItens().getItens().size());
		umUsuario.cadastrarItem("Violetas na Janela", "livro", "Livro Espirita");
		assertEquals(1, umUsuario.gerenteDeItens().getItens().size());
		umUsuario.cadastrarItem("Joao e o pe de feijao", "livro", "Livro Infantil");
		assertEquals(2, umUsuario.gerenteDeItens().getItens().size());
		
		//Testando que nao pode adicionar um item ja existente
		umUsuario.cadastrarItem("Joao e o pe de feijao", "livro", "Livro Infantil");
		assertEquals(2, umUsuario.gerenteDeItens().getItens().size());
	}
	
	@Test
	public void testaApagarItem() throws Exception {
		Item umItem = new Item("Toy Story", outrasCategorias, "Animacao", umUsuario);
		Item outroItem = new Item("Codigo da Vinci", outrasCategorias, "Livro de Ficcao", umUsuario);
		umUsuario.cadastrarItem("Toy Story", "Jogo Filme", "Animacao");
		umUsuario.cadastrarItem("Codigo da Vinci", "Jogo Filme", "Livro de Ficcao");
		assertEquals(2, umUsuario.gerenteDeItens().getItens().size());
		umUsuario.apagarItem(umItem);
		assertEquals(1, umUsuario.gerenteDeItens().getItens().size());
		umUsuario.apagarItem(outroItem);
		assertEquals(0, umUsuario.gerenteDeItens().getItens().size());
		try {
			umUsuario.apagarItem(umItem);
			fail("Esperava excecao de item inexistente");
		} catch (ItemInvalidoException ex) {
			assertEquals("Item inexistente", ex.getMessage());
		}
		
	}
	
	@Test
	public void testaRequisitarAmizade() throws Exception {
		umUsuario.requisitarAmizade(outroUsuario);
		assertEquals(1, outroUsuario.gerenteDeAmigos().getSolicitacoesDeAmizade().size());
		
		try {
			umUsuario.requisitarAmizade(outroUsuario);
			fail("Esperava excecao de requisicao ja solicitada.");
		} catch (Exception ex) {
			assertEquals("Mensagem errada.", ex.getMessage(), "Requisição já solicitada");
		}
		
		outroUsuario.aprovarAmizade(umUsuario);
		assertEquals(1, umUsuario.gerenteDeAmigos().getAmigos().size());
		
		try {
			umUsuario.requisitarAmizade(outroUsuario);
			fail("Esperava excecao de usuarios ja serem amigos.");
		} catch (Exception ex) {
			assertEquals("Mensagem errada.", ex.getMessage(), "Os usuários já são amigos");
		}
	}
	
	@Test
	public void testaAprovarAmizade() throws Exception {
		umUsuario.requisitarAmizade(outroUsuario);
		outroUsuario.aprovarAmizade(umUsuario);
		assertEquals(1, umUsuario.gerenteDeAmigos().getAmigos().size());
		assertEquals(1, outroUsuario.gerenteDeAmigos().getAmigos().size());
	}
	
	@Test
	public void testaRejeitarAmizade() throws Exception {
		umUsuario.requisitarAmizade(outroUsuario);
		assertEquals(1, outroUsuario.gerenteDeAmigos().getSolicitacoesDeAmizade().size());
		outroUsuario.rejeitarAmizade(umUsuario);
		assertEquals(0, outroUsuario.gerenteDeAmigos().getSolicitacoesDeAmizade().size());
		assertEquals(0, umUsuario.gerenteDeAmigos().getAmigos().size());
		assertEquals(0, outroUsuario.gerenteDeAmigos().getAmigos().size());
	}
	
	@Test
	public void testaDesfazerAmizade() throws Exception {
		umUsuario.requisitarAmizade(outroUsuario);
		outroUsuario.aprovarAmizade(umUsuario);
		umUsuario.desfazerAmizade(outroUsuario);
		assertEquals(0, umUsuario.gerenteDeAmigos().getAmigos().size());
		assertEquals(0, outroUsuario.gerenteDeAmigos().getAmigos().size());
	}
	
	@Test
	public void testaRequisitarItem() throws Exception {
		requisitaAmizades();
		Item item = new Item("nome", categorias, "descricao", umUsuario);
		umUsuario.cadastrarItem("nome", "Filme Livro", "descricao");
		outroUsuario.requisitarItem(item, 7);
		assertEquals(1, umUsuario.gerenteDeEmprestimos().getSolicitacoesDeEmprestimo().size());
	}

	@Test
	public void testaAceitarSolicitacaoDeItem() throws Exception {
		requisitaAmizades();
		umUsuario.cadastrarItem("nome", "Filme Livro", "descricao");
		Item deUmUsuario = umUsuario.gerenteDeItens().getItens().get(0);
		outroUsuario.requisitarItem(deUmUsuario, 7);
		umUsuario.aceitarSolicitacaoDeItem(deUmUsuario);
		assertEquals(outroUsuario, umUsuario.gerenteDeEmprestimos().getEmprestimo(deUmUsuario).getBeneficiado());
	}

	@Test
	public void testaRejeitarSolicitacaoDeItem() throws Exception {
		requisitaAmizades();
		Item item = new Item("nome", outrasCategorias, "descricao", umUsuario);
		umUsuario.cadastrarItem("nome", "Jogo Filme", "descricao");
		outroUsuario.requisitarItem(item, 7);
		umUsuario.rejeitarSolicitacaoDeItem(item);
	}
	
	@Test
	public void testaDevolverItem() throws Exception {
		requisitaAmizades();
		umUsuario.cadastrarItem("nome", "Filme Livro", "descricao");
		Item deUmUsuario = umUsuario.gerenteDeItens().getItens().get(0);
		outroUsuario.requisitarItem(deUmUsuario, 7);
		umUsuario.aceitarSolicitacaoDeItem(deUmUsuario);
		outroUsuario.devolverItem(deUmUsuario);
		assertEquals(1, umUsuario.gerenteDeEmprestimos().getNotificacoesDeDevolucao().size());
		
	}
	
	@Test
	public void testaConfirmaDevolucaoItem() throws Exception {
		requisitaAmizades();
		umUsuario.cadastrarItem("nome", "Filme Livro", "descricao");
		Item item = umUsuario.gerenteDeItens().getItens().get(0);
		outroUsuario.requisitarItem(item, 7);
		umUsuario.aceitarSolicitacaoDeItem(item);
		outroUsuario.devolverItem(item);
		umUsuario.confirmaDevolucao(item);
		assertEquals(0, umUsuario.gerenteDeEmprestimos().getNotificacoesDeDevolucao().size());
		assertTrue(item.estaDisponivel());
	}
	
	@Test
	public void testaRequisitarDevolucaoItem() throws Exception {
		requisitaAmizades();
		umUsuario.cadastrarItem("Livro", "Filme Livro	", "livro");
		Item item = umUsuario.gerenteDeItens().getItens().get(0);
		outroUsuario.requisitarItem(item, 4);
		umUsuario.aceitarSolicitacaoDeItem(item);
		umUsuario.requisitarDevolucaoDeItem(item);
		assertEquals(1, outroUsuario.gerenteDeEmprestimos().getRequisicoesDeDevolucao().size());
	}
	
	@Test 
	public void testaRegistraInteresseEmItem() throws Exception {
		requisitaAmizades();
		umUsuario.cadastrarItem("nome", "Filme Livro", "descricao");
		Item item = umUsuario.gerenteDeItens().getItens().get(0);
		try {
			umUsuario.registraInteresseEmItem(item);
			fail("Esperava uma excecao de dono invalido!");
		} catch (DonoInvalidoException ex) {
			assertEquals("Mensagem errada", "O usuário não pode registrar interesse no próprio item", ex.getMessage());
		} 
		try {
			outroUsuario2.registraInteresseEmItem(item);
			fail("Esperava excecao de item disponivel!");
		} catch (Exception ex) {
			assertEquals("Mensagem errada", "Esse item esta disponivel!", ex.getMessage());
		}
		outroUsuario.requisitarItem(item, 5);
		umUsuario.aceitarSolicitacaoDeItem(item);
		outroUsuario2.registraInteresseEmItem(item);
		assertEquals(1, item.getInteressados().size());
	}

	@Test
	public void testaPesquisaItemDataCriacao() throws Exception {
		requisitaAmizades(); 
		cadastraItens();
		Item itemOutro = new Item("Morangos mofados2", categorias, "Caio Fernando", outroUsuario2);
		//incrementando a data de criacao do item de outroUsuario2 para ficar maior que a de umUsuario
		itemOutro.getDataDeCriacao().add(Calendar.DAY_OF_YEAR, 3);

		//ORDEM CRESCENTE
		outroUsuario.pesquisaItem("morangos", "nome", Ordem.CRESCENTE.name().toLowerCase(), BaseDePesquisa.DATACRIACAO.name().toLowerCase());
		assertEquals("Morangos mofados; Morangos mofados2", outroUsuario.pesquisaItem("morangos", "nome", Ordem.CRESCENTE.name(), BaseDePesquisa.DATACRIACAO.name()));
		assertEquals("Nenhum item encontrado", outroUsuario.pesquisaItem("nada encontrado","descricao", Ordem.CRESCENTE.name(),BaseDePesquisa.DATACRIACAO.name()));
		//ORDEM DECRESCENTE
		assertEquals("Morangos mofados2; Morangos mofados", outroUsuario.pesquisaItem("morangos","nome", Ordem.DECRESCENTE.name(), BaseDePesquisa.DATACRIACAO.name()));
		assertEquals("Nenhum item encontrado", outroUsuario.pesquisaItem("nada encontrado","categoria", Ordem.DECRESCENTE.name(), BaseDePesquisa.DATACRIACAO.name()));
	}
	
	@Test
	public void testaPesquisaItemReputacaoDono() throws Exception {
		requisitaAmizades();
		cadastraItens();
		Item.resetaGeradorID();
		Item itemUsuario = new Item("Morangos mofados", categorias, "Caio Fernando", umUsuario);
		Item itemOutro = new Item("Morangos mofados2", categorias, "Caio Fernando", outroUsuario2);
		outroUsuario.requisitarItem(itemUsuario, 2);
		outroUsuario.requisitarItem(itemOutro, 4);
		umUsuario.aceitarSolicitacaoDeItem(itemUsuario);
		outroUsuario2.aceitarSolicitacaoDeItem(itemOutro);
		//emprestimo devolvido por outroUsuario
		outroUsuario.devolverItem(itemUsuario);
		//emprestimo cancelado por outroUsuario2
		outroUsuario2.requisitarDevolucaoDeItem(itemOutro);
		
		//ORDEM CRESCENTE
		assertEquals("Morangos mofados2; Morangos mofados", outroUsuario.pesquisaItem("morangos", "nome", Ordem.CRESCENTE.name(), BaseDePesquisa.REPUTACAO.name()));
		assertEquals("Nenhum item encontrado", outroUsuario.pesquisaItem("nada encontrado", "nome", Ordem.CRESCENTE.name(), BaseDePesquisa.REPUTACAO.name()));
		//ORDEM DECRESCENTE
		assertEquals("Morangos mofados; Morangos mofados2", outroUsuario.pesquisaItem("morangos", "nome", Ordem.DECRESCENTE.name(), BaseDePesquisa.REPUTACAO.name()));
		assertEquals("Nenhum item encontrado", outroUsuario.pesquisaItem("nada encontrado", "nome", Ordem.DECRESCENTE.name(), BaseDePesquisa.REPUTACAO.name()));
	}
	
	@Test 
	public void testaEnviaMensagem() throws Exception {
		requisitaAmizades();
		Item item = new Item("Velozes e Furiosos", categorias, "Filme aventura", outroUsuario);
		
		umUsuario.enviaMensagem(TipoDeMensagem.NEGOCIACAO, "Livro", "Me empresta o livro", outroUsuario); 
		umUsuario.enviaMensagem(TipoDeMensagem.NEGOCIACAO, "Livro", "Quero aquele livro", outroUsuario);
		umUsuario.enviaMensagem(TipoDeMensagem.OFFTOPIC, "Beleza", "Tudo bom?", outroUsuario); 
		umUsuario.enviaMensagem(TipoDeMensagem.OFFTOPIC, "Beleza", "Em ordem?", outroUsuario);
		umUsuario.enviaMensagem(TipoDeMensagem.OFFTOPIC, "Raxa", "Vai jogar amanha?", outroUsuario); 
		outroUsuario2.enviaMensagem(TipoDeMensagem.OFFTOPIC, "Raxa", "Tu vai ou nao?", outroUsuario);
		umUsuario.requisitarItem(item, 10);
		
		assertEquals("{NEGOCIACAO = Empréstimo do item Velozes e Furiosos a Pedro: [Pedro solicitou o empréstimo do " +
				     "item Velozes e Furiosos]; Livro: [Me empresta o livro, Quero aquele livro]; }{OFFTOPIC = Beleza:" +
				     " [Tudo bom?, Em ordem?]; Raxa: [Vai jogar amanha?, Tu vai ou nao?]; }"
				     , Visualizador.visualizaMensagens(outroUsuario));
	}
	
	
	@Test
	public void testaHistoricoAtividades() throws Exception{
		requisitaAmizades();
		assertEquals("Pedro e Lilia são amigos agora; Pedro e Isabelly são amigos agora", umUsuario.getHistoricoDeAtividades().historicoAtividades(umUsuario));
		assertEquals("Pedro e Lilia são amigos agora; Isabelly e Lilia são amigos agora; Lilia e Isabelly são amigos agora; Pedro e Isabelly são amigos agora", umUsuario.getHistoricoDeAtividades().historicoConjunto(umUsuario));
		assertEquals("Isabelly e Lilia são amigos agora; Isabelly e Pedro são amigos agora", outroUsuario.getHistoricoDeAtividades().historicoAtividades(outroUsuario));
		assertEquals("Pedro e Lilia são amigos agora; Lilia e Pedro são amigos agora; Isabelly e Lilia são amigos agora; Isabelly e Pedro são amigos agora", outroUsuario.getHistoricoDeAtividades().historicoConjunto(outroUsuario));
		assertEquals("Lilia e Pedro são amigos agora; Lilia e Isabelly são amigos agora", outroUsuario2.getHistoricoDeAtividades().historicoAtividades(outroUsuario2));
		assertEquals("Lilia e Pedro são amigos agora; Lilia e Isabelly são amigos agora; Isabelly e Pedro são amigos agora; Pedro e Isabelly são amigos agora", outroUsuario2.getHistoricoDeAtividades().historicoConjunto(outroUsuario2));
		cadastraItens();
		assertEquals("Pedro cadastrou Morangos mofados; Pedro e Lilia são amigos agora; Pedro e Isabelly são amigos agora", umUsuario.getHistoricoDeAtividades().historicoAtividades(umUsuario));
		assertEquals("Lilia cadastrou Morangos mofados2; Pedro cadastrou Morangos mofados; Pedro e Lilia são amigos agora; Isabelly e Lilia são amigos agora; Lilia e Isabelly são amigos agora; Pedro e Isabelly são amigos agora", umUsuario.getHistoricoDeAtividades().historicoConjunto(umUsuario));
		assertEquals("Lilia cadastrou Morangos mofados2; Lilia e Pedro são amigos agora; Lilia e Isabelly são amigos agora", outroUsuario2.getHistoricoDeAtividades().historicoAtividades(outroUsuario2));
		assertEquals("Lilia cadastrou Morangos mofados2; Pedro cadastrou Morangos mofados; Lilia e Pedro são amigos agora; Lilia e Isabelly são amigos agora; Isabelly e Pedro são amigos agora; Pedro e Isabelly são amigos agora", outroUsuario2.getHistoricoDeAtividades().historicoConjunto(outroUsuario2));
	}
	
	
	@Test
	public void testaPublicarPedido() throws NomeInvalidoException, DescricaoInvalidaException, InterruptedException{
		try{
			umUsuario.publicarPedido("", "Descricao");
			fail("Esperava uma excecao de Nome invalido!");
		}catch(NomeInvalidoException ex){
			assertEquals("Mensagem errada", "Nome inválido", ex.getMessage());
		}
		try{
			umUsuario.publicarPedido(null, "Descricao");
			fail("Esperava uma excecao de Nome invalido!");
		}catch(NomeInvalidoException ex){
			assertEquals("Mensagem errada", "Nome inválido", ex.getMessage());
		}
		try{
			umUsuario.publicarPedido("Nome", "");
			fail("Esperava uma excecao de Descricao invalida!");
		}catch(DescricaoInvalidaException ex){
			assertEquals("Mensagem errada", "Descrição inválida", ex.getMessage());
		}
		try{
			umUsuario.publicarPedido("Nome", null);
			fail("Esperava uma excecao de Descricao invalida!");
		}catch(DescricaoInvalidaException ex){
			assertEquals("Mensagem errada", "Descrição inválida", ex.getMessage());
		}
		
		umUsuario.publicarPedido("Harry", "Livro de Ficcao");
		assertEquals("Pedro precisa do item Harry", umUsuario.getHistoricoDeAtividades().historicoAtividades(umUsuario));
		assertEquals("Pedro precisa do item Harry", umUsuario.getHistoricoDeAtividades().historicoConjunto(umUsuario));
		umUsuario.publicarPedido("Senhor dos Aneis", "Filme de Ficcao");
		assertEquals("Pedro precisa do item Senhor dos Aneis; Pedro precisa do item Harry", umUsuario.getHistoricoDeAtividades().historicoAtividades(umUsuario));
		assertEquals("Pedro precisa do item Senhor dos Aneis; Pedro precisa do item Harry", umUsuario.getHistoricoDeAtividades().historicoConjunto(umUsuario));
	}
	
	@Test
	public void testaOferecerItem() throws Exception{
		try{
			umUsuario.oferecerItem(outroUsuario, "");
			fail("Esperava uma excecao de Nome invalido!");
		}catch(NomeInvalidoException ex){
			assertEquals("Mensagem errada", "Nome inválido", ex.getMessage());
		}
		
		cadastraItens();
		outroUsuario.publicarPedido("Morangos mofados", "Descricao");
		umUsuario.oferecerItem(outroUsuario, "Morangos mofados");
		assertEquals("{OFFTOPIC = O usuário Pedro ofereceu o item Morangos mofados: [Item oferecido: Morangos mofados - Caio Fernando]; }", Visualizador.visualizaMensagens(outroUsuario));
		umUsuario.publicarPedido("Morangos mofados2", "Descricao");
		outroUsuario2.oferecerItem(umUsuario, "Morangos mofados2");
		assertEquals("{OFFTOPIC = O usuário Lilia ofereceu o item Morangos mofados2: [Item oferecido: Morangos mofados2 - Caio Fernando]; O usuário Pedro ofereceu o item Morangos mofados: " +
				     "[Item oferecido: Morangos mofados - Caio Fernando]; }", Visualizador.visualizaMensagens(umUsuario));
	}
	
	@Test
	public void testaEquals() throws Exception {
		Usuario isabelly = new Usuario("Isabelly", "isabellylr", "Bodocongo");
		assertTrue(umUsuario.equals(umUsuario));
		assertFalse(umUsuario.equals(outroUsuario));
		assertTrue(isabelly.equals(outroUsuario));
		assertTrue(outroUsuario.equals(isabelly));
	}
	
	private void requisitaAmizades() throws Exception {
		outroUsuario.requisitarAmizade(umUsuario);
		umUsuario.aprovarAmizade(outroUsuario);
		outroUsuario2.requisitarAmizade(outroUsuario);
		outroUsuario.aprovarAmizade(outroUsuario2);
		outroUsuario2.requisitarAmizade(umUsuario);
		umUsuario.aprovarAmizade(outroUsuario2);
	}
	
	private void cadastraItens() throws Exception {
		Item.resetaGeradorID();
		umUsuario.cadastrarItem("Morangos mofados", "Filme Livro", "Caio Fernando");
		outroUsuario2.cadastrarItem("Morangos mofados2", "Filme Livro", "Caio Fernando");
	}	
}